/*
*
==============================================================================
*  Name        : 
*  Part of     : Omen Game Engine - OpenGL Plugin
*  Description : 
*  Version     : 1.0
*
*  Copyright (c) 2013 Lauri Kortevaara.
* 
==============================================================================
*/

#include "SystemInfo.h"

// DirectX Graphics Infrastructure (DXGI) header
#include <DXGI.h>

// STL headers
#include <iostream>
#include <vector>
#include <algorithm>

// OpenGL headers
#include <GL/GL.h>

// Static class variables
SystemInfo::OpenGLExtensionVector   SystemInfo::gl_extensions;
std::vector<VideoMode>              SystemInfo::supportedVideoModes;

//=============================================================================
/**
** <summary> </summary>
**
*/
SystemInfo::SystemInfo(void)
{
    std::wcout << L"SystemInfo Singleton Created!" << std::endl;
}


//=============================================================================
/**
** <summary> </summary>
**
*/
SystemInfo::~SystemInfo(void)
{
    std::wcout << L"SystemInfo Singleton deleted!" << std::endl;
}

//=============================================================================
/**
** <summary> </summary>
**
*/
static bool videoModeLessThan(VideoMode& first, VideoMode& second)
{
    bool bIsLess = false;
    if( first.width*first.height < second.width*second.height || 
        first.width*first.height == second.width*second.height && first.bitsPerPixel < second.bitsPerPixel ||
        first.width*first.height == second.width*second.height && first.bitsPerPixel == second.bitsPerPixel && first.displayFrequency < second.displayFrequency )
        bIsLess = true;
    return bIsLess;
}

//=============================================================================
/**
** <summary> </summary>
**
*/
void SystemInfo::getSupportedDisplayModes()
{
    std::wcout << L"Supported display modes:" << std::endl;

    DEVMODE devMode;
    for( int i=0; EnumDisplaySettings( NULL, i, &devMode ); i++ )
    {
        VideoMode mode;
        mode.deviceName = std::wstring(reinterpret_cast<const WCHAR*>(devMode.dmDeviceName));
        mode.bitsPerPixel = devMode.dmBitsPerPel;
        mode.width = devMode.dmPelsWidth;
        mode.height = devMode.dmPelsHeight;
        mode.displayFrequency = devMode.dmDisplayFrequency;
        supportedVideoModes.push_back( mode );
    }

    std::sort( supportedVideoModes.begin(), supportedVideoModes.end(), &videoModeLessThan );

    for( std::vector<VideoMode>::iterator iter = supportedVideoModes.begin(); iter != supportedVideoModes.end(); ++iter )
    {
        std::wcout << L"\tDevice: " << iter->deviceName << L", ";
        std::wcout << L"\tSize(px): " << iter->width<< L"x" << iter->height << L", ";
        std::wcout << L"(" << iter->bitsPerPixel << L" bit),\t";
        std::wcout << iter->displayFrequency << L" Hertz";
        std::wcout << std::endl;
    }
}


//=============================================================================
/**
** <summary> Function prints out Display Card Model information </summary>
**
*/
void SystemInfo::getDisplayCardInfo()
{
    /*IDXGIFactory* pDXGIFactory;
    CreateDXGIFactory1( __uuidof(IDXGIFactory), (void**)&pDXGIFactory );

    IDXGIAdapter* pDXGIAdapter;

    HRESULT hr = pDXGIFactory->EnumAdapters( 0, &pDXGIAdapter );
    if( S_OK == hr )
    {
        DXGI_ADAPTER_DESC adaptDesc;
        if ( pDXGIAdapter->GetDesc( &adaptDesc ) == S_OK )
        {
            std::wstring cardDescr = reinterpret_cast< const WCHAR* >(adaptDesc.Description);
            std::wcout << L"Display card: " << cardDescr << std::endl;
            std::wcout << L"\tDisplay memory: " << adaptDesc.DedicatedVideoMemory/(1024*1024) << " Mb" << std::endl;
            std::wcout << L"\tTotal Display memory: " << adaptDesc.SharedSystemMemory / (1024*1024) << " Mb" << std::endl << std::endl;
        }
    }*/
}

//=============================================================================
/**
**  GetGPUMemoryInfo_Nvidia
**
** <summary> Function will get memory information about Nvidia Display cards </summary>
**
**  \param i_mem_total [out]: total memory on GPU in kilo bytes
**  \param i_mem_available [out]: available memory on GPU in kilo bytes
**  \ret bool: true if nvidia gpu memory info extension is available, false otherwise
*/
bool SystemInfo::GetGPUMemoryInfo_Nvidia( int& i_mem_total, int& i_mem_available )
{
    bool bOk = false;
    if( std::find(SystemInfo::gl_extensions.begin(), SystemInfo::gl_extensions.end(), L"GL_NVX_gpu_memory_info" ) != SystemInfo::gl_extensions.end() )
    {
        #define GL_GPU_MEM_INFO_TOTAL_AVAILABLE_MEM_NVX 0x9048
        #define GL_GPU_MEM_INFO_CURRENT_AVAILABLE_MEM_NVX 0x9049

        glGetIntegerv(GL_GPU_MEM_INFO_TOTAL_AVAILABLE_MEM_NVX, &i_mem_total);
        glGetIntegerv(GL_GPU_MEM_INFO_CURRENT_AVAILABLE_MEM_NVX, &i_mem_available);

        bOk = true;
    }

    return bOk;
}

//=============================================================================
/**
** <summary> Function will get an memory information about ATI/AMD Display cards </summary>
*/
void SystemInfo::GetGPUMemoryInfo_AtiAmd()
{
    /*
    GLuint uNoOfGPUs = wglGetGPUIDsAMD( 0, 0 );
    GLuint* uGPUIDs = new GLuint[uNoOfGPUs];
    wglGetGPUIDsAMD( uNoOfGPUs, uGPUIDs );

    GLuint uTotalMemoryInMB = 0;
    wglGetGPUInfoAMD( uGPUIDs[0],  WGL_GPU_RAM_AMD,
                                   GL_UNSIGNED_INT,
                                   sizeof( GLuint ),
                                   &uTotalMemoryInMB );

    GLint nCurAvailMemoryInKB = 0;
    glGetIntegerv( GL_TEXTURE_FREE_MEMORY_ATI,&nCurAvailMemoryInKB );
    */
}

//=============================================================================
/**
** <summary> Function will get an enumerate the supported OpenGL extensions </summary>
*/
bool SystemInfo::getOpenGLExtensions()
{
#define USE_WGLEXT 1
    const char *str;
    std::wstring splitString;
    str = (const char*)glGetString(GL_EXTENSIONS);
    std::wcout << L"OpengL Extensions available: " << std::endl;

    for( auto iter = SystemInfo::gl_extensions.begin(); iter != SystemInfo::gl_extensions.end(); ++iter )
        std::wcout << *iter << std::endl;


#ifdef GLSL_WINDOWS
#ifdef USE_WGLEXT
    PFNWGLGETEXTENSIONSSTRINGARBPROC   wglGetExtensionsStringARB;
    wglGetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC)wglGetProcAddress("wglGetExtensionsStringARB");
    
    if(wglGetExtensionsStringARB == NULL)
    {
       return false;
    }
    
    str = (const GLubyte*) wglGetExtensionsStringARB(wglGetCurrentDC()); 
    if (strstr((const char *)str, extension_name) != 0) return true;
#endif    
#else
// ignore additional platform extensions
#endif

    GLint total, avail;
    if( GetGPUMemoryInfo_Nvidia(total,avail) )
    {
        std::wcout << L"Total Memory on GPU: " << total/1024 << " Mb" << std::endl << "Available memory on GPU: " << avail/1024 << " Mb" << std::endl;
    }
    return false;
}